/**
 * Recife Action Tools - RAT
 * Centro de Inform�tica - CIn - UFPE
 *
 * Biblioteca para Gera��o de Analisador L�xico
 */

package br.ufpe.abaco.ParserGen.lexer;

import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Vector;

import br.ufpe.abaco.ParserGen.parser.TerminalSymbol;

class LexicalProcessor implements java.io.Serializable  {

	CharManager classifier = new CharManager();
	PrintWriter pw;
	RegExp r;

	void findClasses(RegExp src) {

		if (src instanceof CharExp) {
			CharExp esrc = (CharExp) src;
			classifier.insertClass(esrc.start, esrc.start);

			if (false) {
				System.out.println(
					"inserindo : "
						+ ((char) esrc.start));
				System.out.println("Resultado : " + classifier);
			}
		} else {
			for (int c = 0; c < src.subTerms.length; c++) {
				findClasses(src.subTerms[c]);
			}
		}
	}

	int nDefinitions = 0;

	void findDefinitions(RegExp src) {

		if (src instanceof CharExp || src instanceof FinalExp) {
			nDefinitions++;
		} else {
			for (int c = 0; c < src.subTerms.length; c++) {
				findDefinitions(src.subTerms[c]);
			}
		}
	}

	State startState;
	int lastState = 0;

	class State implements java.io.Serializable {
		int state;
		boolean processed = false;
		RegExp definition[];
		boolean finalState;
		State links[];
		HashSet tokens; //cont�m TerminalSymbols
		int size;

		//Vector links = new Vector();

		State(RegExp def[]) {
			size = classifier.size();
			links = new State[size];
			definition = def;
			finalState = false;
		}

		void process() {
			int c1;
			int c2;
			int c3;
			boolean foundToken = false;
			RegExp linksDefs[][] = new RegExp[size][];
			boolean replaced[] = new boolean[size];

			for (c1 = 0; c1 < definition.length; c1++) {

				if (definition[c1] instanceof CharExp) {
					CharExp cexp = (CharExp) definition[c1];
					int start = classifier.getClass(cexp.start);
					int end = classifier.getClass(cexp.start);
					RegExp follow[] = cexp.follow;

					for (c2 = start; c2 <= end; c2++) {
						replaced[c2] = false;
					}

					for (c2 = start; c2 <= end; c2++) {

						if (start <= c2 && c2 <= end) {
							if (linksDefs[c2] == null) {
								linksDefs[c2] = follow;
							} else if (!replaced[c2]) {
								RegExp src[] = linksDefs[c2];
								RegExp concat[] =
									ArrayOps.concat(linksDefs[c2], follow);

								for (c3 = c2; c3 <= end; c3++) {

									if (linksDefs[c3] == src) {
										replaced[c3] = true;
										linksDefs[c3] = concat;
									}
								}
							}
						}
					}
				} else if (
					definition[c1] instanceof FinalExp
						|| definition[c1] instanceof RejectExp) {
					foundToken = true;
				}
			}

			if (foundToken) {
				finalState = true;
				tokens = new HashSet();
				foundToken = false;

				for (c1 = 0; c1 < definition.length; c1++) {
					if (definition[c1] instanceof FinalExp) {
						/*						tokens[n_tokens++] =
						       ((FinalExp) definition[c1]).tokenType;*/
						tokens.add(((FinalExp) definition[c1]).tokenType);
					} else if (definition[c1] instanceof RejectExp) {
						TerminalSymbol rejectedSymbol =
							((RejectExp) definition[c1]).tokenType;
						tokens.remove(rejectedSymbol);
					}
				}

			} else {
				//tokens = new int[0];
				//				tokens = new TerminalSymbol[0];
				tokens = new HashSet();
				tokens.add(new TerminalSymbol[0]);
			}

			for (c1 = 0; c1 < size; c1++) {
				replaced[c1] = linksDefs[c1] == null;
			}

			for (c1 = 0; c1 < size; c1++) {
				if (!replaced[c1]) {
					RegExp src[] = linksDefs[c1];
					State dest = getState(linksDefs[c1]);
					links[c1] = dest;

					for (c2 = c1 + 1; c2 < size; c2++) {
						if (linksDefs[c2] == src) {
							links[c2] = dest;
							replaced[c2] = true;
						}
					}
				}
			}
		}

/*		public String toString() {
			String resul =
				"State" + (finalState ? "*" : "") + " : " + state + "\n";
			resul += "Definitions : " + ArrayOps.print(definition) + "\n";

			for (int c = 0; c < links.length; c++) {
				if (links[c] != null) {
					resul += classifier.printClass(c)
						+ " -> "
						+ links[c].state
						+ "\n";
				}
			}

			return resul;
		}
*/
		TerminalSymbol[] cachedResult = null;
		TerminalSymbol[] getTokensTypes() {
			if (cachedResult == null) {
				cachedResult = new TerminalSymbol[tokens.size()];
				this.tokens.toArray(cachedResult);
				for (int c1 = 0; c1 < cachedResult.length; c1++)
					for (int c2 = c1; c2 < cachedResult.length; c2++)
						if (cachedResult[c1].priority()
							> cachedResult[c2].priority()) {
							TerminalSymbol temp = cachedResult[c1];
							cachedResult[c1] = cachedResult[c2];
							cachedResult[c2] = temp;
						}
			}

			return cachedResult;

		}

	}

	int getStateCalls = 0;
	State states[] = new State[1000];
	int nStates = 0;

	State getState(RegExp def[]) {
		getStateCalls++;

		//if (false) {
		//	System.out.println("Procurando : " + ArrayOps.print(def));
		//}

		int min = 0;
		int max = nStates - 1;
		int med;
		int comp;

		while (min <= max) {
			med = (min + max) / 2;
			comp = ArrayOps.compare(def, states[med].definition);

			if (comp == 0) {
				return states[med];
			} else if (comp > 0) {
				min = med + 1;
			} else if (comp < 0) {
				max = med - 1;
			}
		}

		State novo = new State(def);

		for (int c = nStates; c > 0; c--) {

			if (ArrayOps.compare(def, states[c - 1].definition) < 0) {
				states[c] = states[c - 1];
				states[c].state = c;
			} else {
				states[c] = novo;
				states[c].state = c;
				//System.out.println("Inserting at pos = " + c + "\n" );
				nStates++;
				return novo;
			}
		}

		states[0] = novo;
		states[0].state = 0;
		nStates++;

		return novo;
	}

	public LexicalProcessor() {
	}

	public LexicalProcessor(RegExp src) {
		try {
			//pw = new PrintWriter(new FileOutputStream("LEXER.TXT"));
		} catch (Exception e) {
			e.printStackTrace();
		}
		setRegularExpression(src);

	}

	public void setRegularExpression(RegExp src) {
		r = src;		
		findClasses(src);
		classifier.compile();
		
		int c;
		
		startState = getState(src.first);

		{
			boolean found = true;

			while (found) {
				found = false;

				for (c = 0; c < nStates; c++) {
					if (!states[c].processed) {
						states[c].processed = true;
						states[c].process();
						found = true;
					}
				}
			}
		}

		for (c=0;c<nStates;c++) {
			states[c].definition = null;
		}
		
		
		if (false) {
			System.out.println("Classes : " + classifier);
			System.out.println(src);
			System.out.println(this);
		}
		

	}

	boolean process(String input) {
		int oldPos = -1;
		State oldState = null;
		byte inputArray[] = input.getBytes();
		int pos = 0;
		int corr;
		int table[] = classifier.lookupTable;
		int size = inputArray.length;
		State currState = startState;

		while (pos < size) {
			corr = table[inputArray[pos]];

			if (corr == -1) {
				return false;
			}

			currState = currState.links[corr];

			if (currState == null) {
				return false;
			}

			pos++;
		}

		return currState.finalState;
	}

	boolean process(String input, Vector tokens) {
		return process(input, tokens, -1);
	}

    boolean process(String input, Vector tokens, int line) {
	
	byte inputArray[] = input.getBytes();
	int table[] = classifier.lookupTable;
	int size = inputArray.length;
	int finalStatePos = -1;
	
	State finalState = null;
	Token lastToken = null;
	int start = 0;
	int pos = 0;
	int lineNumber = 0;
	int colunm = 0;
	State currState = startState;
	int corr;
	
	while (pos <= size) {
	    if (pos == size) {
		if (finalState == null) {					
		    //System.out.println("expressao regular: " + r + ", entrada: " + input);
		    return false;
		} else {
		    TerminalSymbol[] oldTokens = finalState.getTokensTypes();
		    
		    if (!(oldTokens.length == 1
			  && (oldTokens[0].equals(TerminalSymbol.BLANK)
			      /*|| oldTokens[0].equals(TerminalSymbol.COMMENT*)*/))) {
			Token novo =
			    new Token(
				      oldTokens,
				      new String(
						 inputArray,
						 start,
						 finalStatePos - start + 1),
				      colunm,
				      line+lineNumber,start,finalStatePos);
			
			if (lastToken != null) {
			    lastToken.next = novo;
			}
			
			lastToken = novo;
			tokens.addElement(novo);
		    }
		    
		    finalState = null;
		    currState = startState;
		    start = pos = finalStatePos + 1;
		    
		    //adiciona o lexema que indica o final da entrada
		    //tokens.addElement(EOF);
		    
		    return true;
		}
	    } else {
	    if (inputArray[pos]=='\n') {
	    	colunm = 0;
	    	lineNumber++;
	    } else
	    	colunm++;
		corr = table[inputArray[pos]];
		
		if (corr == -1) {
		    //System.out.println("expressao regular: " + r + ", entrada: " + input);
		    return false;
		}
		
		currState = currState.links[corr];
		
		if (currState == null) {
		    if (finalState == null) {
			//System.out.println("expressao regular: " + r + ", entrada: " + input);
			return false;
		    } else {
			TerminalSymbol[] oldTokens = finalState.getTokensTypes();
			
			//System.out.println("Achei = " + new String(inputArray, start, oldPos - start + 1));
			if (!(oldTokens.length == 1
			      && (oldTokens[0].equals(TerminalSymbol.BLANK)
				  /*|| oldTokens[0].equals(
				    TerminalSymbol.COMMENT))*/))) {
			    
			    Token novo =
				new Token(
					  oldTokens,
					  new String(
						     inputArray,
						     start,
						     finalStatePos - start + 1),
					  colunm,
					  line+lineNumber,start,finalStatePos);
			    
			    if (lastToken != null) {
				lastToken.next = novo;
			    }
			    
			    lastToken = novo;
			    
			    tokens.addElement(novo);
			}
			
			finalState = null;
			currState = startState;
			start = pos = finalStatePos + 1;
		    }
		} else {
		    if (currState.finalState) {
			finalState = currState;
			finalStatePos = pos;
		    }
		    pos++;
		}
	    }
	}
	
	//System.out.println("expressao regular: " + r + ", entrada: " + input);
	return currState == startState;
    }
    
    public String toString() {
	String res = "";
	
	for (int c = 0; c < nStates; c++) {
	    res += states[c];
	}
	
	return res;
    }
    
}
